/**
 * To use, log in to your Google Voice account. Go to Settings, then Call Widgets.
 * In the text box, there is some HTML code to embed in your web page. There should
 * be a PARAM element that contains the attribute name="FlashVars". Copy the contents
 * of the value attribute. Include this script in the HEAD portion of your document.
 * At the end of the BODY, place the following code:
 *
 * <script type="text/javascript">
 * installClick2Call(phoneNumber, flashVars);
 * </script>
 *
 * Be sure to substitute (or define) the phoneNumber variable with your Google Voice number.
 * Be sure to substitute (or define) the flashVars variable with the value you copied.
 *
 * This will install the click-to-call widget on mouseover for every matching
 * "tel:" link in your document.
 */
 
(function() {

var backdrop,
    widgetURI = 'https://clients4.google.com/voice/embed/webCallButton',
    widgetWidth = 230,
    widgetHeight = 85;

/**
 * Takes a phone number, matches all 'tel:' links
 * in the current document and installs the
 * Google Voice click-to-call widget as a mouseover
 * on those links. The 'maxwait' argument is to specify
 * how long to wait for the Flash widget to load.
 */
this.installClick2Call = // export function
function installClick2Call(number, flashvars, maxwait) {
  var widget = createWidget(flashvars);
  maxwait = maxwait || 20000;  // 20 second default wait
  pollUntilFlashIsLoaded(maxwait, widget, function() {
    if (document.activeElement &&
        document.activeElement == widget)
      document.body.setActive();
    widget.style.visibility = 'hidden';
    installOnTelLinks(number, widget);
  });
}

/**
 * Installs the already loaded widget on all matching
 * 'tel:' links.
 */
function installOnTelLinks(number, widget) {
  var links = document.getElementsByTagName('a');
  var href = "tel:" + number;
  for (var i=0; i<links.length; i++) {
    var link = links[i];
    if (link.href == href)
      installOnTelLink(link, widget);
  }
}

/**
 * Install the widget on mouseover for the given link.
 *
 * Makes extensive use of closures and most likely
 * leaks memory in IE 6.
 * (http://support.microsoft.com/kb/830555)
 */
function installOnTelLink(link, widget) {

  var box;

  // install click-to-call on the link
  if (link.addEventListener) {
    link.addEventListener('mouseover', onmouseover, false);
  } else if (link.attachEvent) {
    link.attachEvent('onmouseover', onmouseover);
  }
  
  function onmouseover(e) {
    //alert(document.activeElement.tagName);
    if (!box) {
      trackPoint(e);
      widget.style.visibility = 'visible';
      installmouseoverevents();
    }
  };

  function installmouseoverevents() {
    box = Box.fromElement(link);  // set bounds for simulated mouseout
    if (document.addEventListener) {
      document.addEventListener('mousemove', onmousemove, false);
      widget.addEventListener('click', onwidgetactivation, true);
    } else if (document.attachEvent) {
      document.attachEvent('onmousemove', onmousemove);
      // you can't capture click events properly in IE
      // but 'onactivate' will let us know that
      // the widget was clicked
      if ('onactivate' in widget)
        widget.attachEvent('onactivate', onwidgetactivation);
    }
  }

  function uninstallmouseoverevents() {
    if (document.removeEventListener) {
      document.removeEventListener('mousemove', onmousemove, false);
      widget.removeEventListener('click', onwidgetactivation, true);
    } else if (document.detachEvent) {
      document.detachEvent('onmousemove', onmousemove);
      if ('onactivate' in widget)
        widget.detachEvent('onactivate', onwidgetactivation);
    } 
    box = null; // part of uninstalling faux 'mouseout'
  }

  function onmousemove(e) {
    if ( !box.contains( trackPoint(e) ) ) {
      uninstallmouseoverevents();
      widget.style.visibility = 'hidden';
    }
  }
  
  // when called on mouse events, this will position the widget
  // under the mouse and return the mouse coords
  function trackPoint(e) {
    var point = eventPoint(e);
    widget.style.left = (point.x - 7) + 'px';
    widget.style.top = (point.y - widget.offsetHeight + 7) + 'px';
    return point;
  }  
  
  function onwidgetactivation() {
    uninstallmouseoverevents();
    // make sure the widget is fully showing
    if (widget.offsetTop < 0) widget.style.top = '0px';
    // help the user not to become distracted
    showBackdrop(function() { widget.style.visibility = 'hidden' });
  } 
}

/**
 * Create the Google Voice Click-to-call widget
 * and append it to the current document.
 *
 * Notice I just use one 'object' element
 * for all browsers.
 * http://www.alistapart.com/articles/byebyeembed/
 *
 * And since it's inserted dynamically,
 * it avoids potential 'click to activate' issues.
 * http://blogs.msdn.com/ie/archive/2007/11/08/
 *   ie-automatic-component-activation-changes-to-ie-activex-update.aspx
 */
function createWidget(flashvars) {  
  var html = '<object type="application/x-shockwave-flash"' +
                    ' data="' + widgetURI + '"' +
                    ' width="' + widgetWidth + '"' +
                    ' height="' + widgetHeight + '"' +
                    ' style="position:absolute;top:0;left:0;z-index:10001;' +                  
                            'top:-' + widgetHeight + 'px">' +
//                            'visibility:hidden">' +
               '<param name="movie" value="' + widgetURI + '" />' +  // for IE
               '<param name="wmode" value="transparent" />' +
               '<param name="flashvars" value="' + flashvars + '" />' +
             '</object>';
  return appendHTML(document.body, html);
}

/**
 * Show a semi-transparent backdrop that will cover the entire document
 * and hide itself (and call callback) when clicked.
 */
function showBackdrop(callback) {
  var d = document;
  // make sure the backdrop is initialized
  if (!backdrop) {
    backdrop = d.createElement('div');
    backdrop.style.position = 'absolute';
    backdrop.style.top = '0px';
    backdrop.style.left = '0px';
    backdrop.style.zIndex = 10000;
    backdrop.style.backgroundColor = 'black';
    backdrop.style.opacity = 0.80;
    backdrop.style.filter =  // idiotic 'filter' style needed for IE
      'progid:DXImageTransform.Microsoft.Alpha(opacity=80)';
    backdrop.style.visibility = 'hidden';
    d.body.appendChild(backdrop);
  }
  // stretch the backdrop to cover the document, then show it
  backdrop.style.width = Math.max(d.documentElement.scrollWidth,d.body.scrollWidth) + 'px';
  backdrop.style.height = Math.max(d.documentElement.scrollHeight,d.body.scrollHeight) + 'px';
  backdrop.style.visibility = 'visible';
  // create a function that will hide the backdrop and fire 'callback' on click
  var finished = function() {
    if (backdrop.removeEventListener) {
      backdrop.removeEventListener('click', finished, false);
    } else if (backdrop.detachEvent) {
      backdrop.detachEvent('onclick', finished);
      finished = null;
    }
    backdrop.style.visibility = 'hidden';
    callback();
  };
  if (backdrop.addEventListener) {
    backdrop.addEventListener('click', finished, false);
  } else if (backdrop.attachEvent) {
    backdrop.attachEvent('onclick', finished);
  }
}

/**
 * Poll up to 3 times a second to check if the flash widget
 * is fully loaded. Once loaded, invoke the callback.
 */
function pollUntilFlashIsLoaded(maxwait, widget, callback) {
  if ( flashIsLoaded(widget) ) {
    callback();
  } else if (maxwait > 0) {
    setTimeout(function() {
      pollUntilFlashIsLoaded(maxwait-300, widget, callback);
    }, 300);
  }
}

/**
 * Returns true if the flash widget is fully loaded,
 * false otherwise.
 */
function flashIsLoaded(widget) {
  return (typeof widget.PercentLoaded != 'undefined' &&
            widget.PercentLoaded() == 100);
}

/**
 * Box 'class'. A box has a coordinate set, width and height.
 */
function Box(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
}

/**
 * Create a Box from the given element.
 * The box's coordinates wil be relative to the current document.
 * http://www.quirksmode.org/js/findpos.html
 */
Box.fromElement = function(el) {
  var posx = posy = 0;
  var parent = el;
  do {
    posx += parent.offsetLeft;
    posy += parent.offsetTop;
  } while (parent = parent.offsetParent);
  return new Box(posx, posy, el.offsetWidth, el.offsetHeight);
}

Box.prototype = {

  /**
   * Returns true if the given point is inside the box.
   */
  contains: function(point) {
    return ( (point.x >= this.x && point.x <= (this.x + this.width) ) &&
             (point.y >= this.y && point.y <= (this.y + this.height) ) );
  }
};

/**
 * Find the mouse position relative to the current document.
 * http://www.quirksmode.org/js/events_properties.html
 */
function eventPoint(e) {
  var posx = posy = 0;
  if (!e) var e = window.event;
  if (e.pageX || e.pageY) 	{
    posx = e.pageX;
    posy = e.pageY;
  } else if (e.clientX || e.clientY) {
    posx = e.clientX + document.body.scrollLeft
           + document.documentElement.scrollLeft;
    posy = e.clientY + document.body.scrollTop
           + document.documentElement.scrollTop;
  }
  return { x: posx, y: posy };
}

/**
 * Appends the given HTML to end of the
 * given element and returns a reference to
 * the new node. This will only work reliably
 * if all HTML is wrapped in a single parent
 * element.
 */
function appendHTML(el, html) {
  if (el.insertAdjacentHTML) {
    el.insertAdjacentHTML('beforeEnd', html);
    return el.lastChild;
  } else {
    var tmp = el.ownerDocument.createElement('div');
    tmp.innerHTML = html;
    return el.appendChild(tmp.firstChild);
  }
}

})();
